package wt

import (
	"context"
	"crypto/tls"
	"sync"
	"time"

	"gitee.com/AlexWoo/arpc/alog"
	"gitee.com/AlexWoo/arpc/anet"
	"github.com/quic-go/quic-go"
	"github.com/quic-go/webtransport-go"
)

func (c *Connection) connect(url string, conf *ClientConf) error {
	alog.LogInfo(c.lctx, "WTClient Start connect [%p]", c)

	qc := &quic.Config{
		HandshakeIdleTimeout: 1 * time.Second,
		Allow0RTT:            true,
		EnableDatagrams:      true,
	}

	tlsConf := &tls.Config{
		InsecureSkipVerify: conf.InsecureSkipVerify,
	}

	client := webtransport.Dialer{
		QUICConfig:      qc,
		TLSClientConfig: tlsConf,
	}

	_, conn, err := client.Dial(context.Background(), url, nil)
	if err != nil {
		return err
	}

	c.conn = conn
	c.remote = conn.RemoteAddr().String()
	c.local = conn.LocalAddr().String()

	alog.LogInfo(c.lctx, "WTClient connect to %s success", url)

	if c.onConnect != nil {
		c.onConnect(c)
	}

	c.loop()

	return nil
}

func WTConnect(addr string, conf *ClientConf, h anet.Handler, wg *sync.WaitGroup,
	onConnect anet.OnConnect, onDisconnect anet.OnDisconnect) *Connection {

	c := newConnection(&conf.Log)
	c.h = h
	c.isClient = true
	c.retryIntervalSec = conf.RetryIntervalMinSec
	c.onConnect = onConnect
	c.onDisconnect = onDisconnect

	wg.Add(1)
	go func(wg *sync.WaitGroup) {
		defer wg.Done()

		for {
			if c.closed == 1 {
				return
			}

			url := "https://" + addr + conf.Path
			alog.LogInfo(c.lctx, "WTClient start connect [%s]", url)
			err := c.connect(url, conf)
			if err == nil {
				c.retryIntervalSec = conf.RetryIntervalMinSec
			} else {
				alog.LogError(c.lctx, "connect [%s] error[%v]", url, err)
			}

			if c.onDisconnect != nil {
				c.onDisconnect(c, err)
			}

			time.Sleep(time.Duration(c.retryIntervalSec) * time.Second)

			if err != nil {
				c.retryIntervalSec = 2 * c.retryIntervalSec
				if c.retryIntervalSec > conf.RetryIntervalMaxSec {
					c.retryIntervalSec = conf.RetryIntervalMaxSec
				}
			}
		}
	}(wg)

	return c
}

func Connect(addr string, conf *anet.Conf, h anet.Handler, wg *sync.WaitGroup,
	onConnect anet.OnConnect, onDisconnect anet.OnDisconnect) anet.Conn {

	cc := &ClientConf{
		Path:                conf.Path,
		RetryIntervalMinSec: conf.RetryIntervalMinSec,
		RetryIntervalMaxSec: conf.RetryIntervalMaxSec,
		Log:                 conf.Log,
	}

	return WTConnect(addr, cc, h, wg, onConnect, onDisconnect)
}
